package tv.acgn.advertisement.publish.service.impl;

import java.util.List;
import java.util.stream.Collectors;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import tv.acgn.advertisement.publish.dao.AdPlanDao;
import tv.acgn.advertisement.publish.dao.AdUnitDao;
import tv.acgn.advertisement.publish.dao.AdUnitDistrictDao;
import tv.acgn.advertisement.publish.dao.AdUnitInterestDao;
import tv.acgn.advertisement.publish.dao.AdUnitKeywordDao;
import tv.acgn.advertisement.publish.dao.CreativeDao;
import tv.acgn.advertisement.publish.dao.CreativeUnitDao;
import tv.acgn.advertisement.publish.dao.dsl.AdUnitDslDao;
import tv.acgn.advertisement.publish.exception.AbstractAdPubException;
import tv.acgn.advertisement.publish.exception.AdPlanNotFoundException;
import tv.acgn.advertisement.publish.exception.AdUnitNotFoundException;
import tv.acgn.advertisement.publish.exception.AdUnitSameNameExistException;
import tv.acgn.advertisement.publish.exception.CreativeNotFoundException;
import tv.acgn.advertisement.publish.po.AdUnitPO;
import tv.acgn.advertisement.publish.po.CreativePO;
import tv.acgn.advertisement.publish.po.CreativeUnitPO;
import tv.acgn.advertisement.publish.po.unit_condition.AdUnitDistrict;
import tv.acgn.advertisement.publish.po.unit_condition.AdUnitInterest;
import tv.acgn.advertisement.publish.po.unit_condition.AdUnitKeyword;
import tv.acgn.advertisement.publish.service.AdUnitService;
import tv.acgn.advertisement.publish.vo.adunit.AdUnitDistrictRequestVO;
import tv.acgn.advertisement.publish.vo.adunit.AdUnitDistrictRequestVO.UnitDistrict;
import tv.acgn.advertisement.publish.vo.adunit.AdUnitDistrictResponseVO;
import tv.acgn.advertisement.publish.vo.adunit.AdUnitInterestRequestVO;
import tv.acgn.advertisement.publish.vo.adunit.AdUnitInterestRequestVO.UnitInterest;
import tv.acgn.advertisement.publish.vo.adunit.AdUnitInterestResponseVO;
import tv.acgn.advertisement.publish.vo.adunit.AdUnitKeywordRequestVO;
import tv.acgn.advertisement.publish.vo.adunit.AdUnitKeywordRequestVO.UnitKeyword;
import tv.acgn.advertisement.publish.vo.adunit.AdUnitKeywordResponseVO;
import tv.acgn.advertisement.publish.vo.adunit.AdUnitRequestVO;
import tv.acgn.advertisement.publish.vo.adunit.AdUnitResponseVO;
import tv.acgn.advertisement.publish.vo.creative.CreativeUnitRequestVO;
import tv.acgn.advertisement.publish.vo.creative.CreativeUnitRequestVO.CreativeUnitItem;
import tv.acgn.advertisement.publish.vo.creative.CreativeUnitResponseVO;

/**
 * @author : junG
 * @program : advertisement-ad-pub-service-impl
 * @description : 推广单元服务实现类
 * @date : 2019-05-26 00:18
 **/

public class AdUnitServiceImpl implements AdUnitService {

    @Autowired
    private AdPlanDao adPlanDao;

    @Autowired
    private AdUnitDao adUnitDao;

    @Autowired
    private AdUnitDslDao adUnitDslDao;

    @Autowired
    private AdUnitKeywordDao adUnitKeywordDao;

    @Autowired
    private AdUnitInterestDao adUnitInterestDao;

    @Autowired
    private AdUnitDistrictDao adUnitDistrictDao;

    @Autowired
    private CreativeDao creativeDao;

    @Autowired
    private CreativeUnitDao creativeUnitDao;

    @Override
    public AdUnitPO getAdUnit(@NotBlank String unitId) throws AbstractAdPubException {
        AdUnitPO adUnitPO = adUnitDao
            .findById(unitId)
            .orElseThrow(AdUnitSameNameExistException::new);
        return adUnitPO;
    }

    @Override
    public AdUnitResponseVO addAdUnit(@Valid AdUnitRequestVO requestVO) throws AbstractAdPubException {

        adPlanDao.findById(requestVO.getPlanId()).orElseThrow(AdPlanNotFoundException::new);

        adUnitDao.findByPlanIdAndUnitName(requestVO.getPlanId(), requestVO.getUnitName())
            .orElseThrow(AdUnitSameNameExistException::new);

        AdUnitPO adUnitPO = new AdUnitPO();
        adUnitPO.setPlanId(requestVO.getPlanId())
            .setUnitName(requestVO.getUnitName())
            .setPositionType(requestVO.getPositionType())
            .setBudget(requestVO.getBudget());

        adUnitDao.save(adUnitPO);

        return AdUnitResponseVO.builder()
            .planId(adUnitPO.getPlanId())
            .unitId(adUnitPO.getUnitId())
            .unitName(adUnitPO.getUnitName())
            .build();
    }

    @Override
    public AdUnitResponseVO updateAdUnit(@Valid AdUnitRequestVO requestVO) throws AbstractAdPubException {
        adPlanDao.findById(requestVO.getPlanId()).orElseThrow(AdPlanNotFoundException::new);

        AdUnitPO oldAdUnitPO = adUnitDao
            .findById(requestVO.getUnitId())
            .orElseThrow(AdUnitNotFoundException::new);

        adUnitDslDao.update(oldAdUnitPO, requestVO);

        return AdUnitResponseVO.builder()
            .unitId(oldAdUnitPO.getUnitId())
            .unitName(requestVO.getUnitName())
            .build();
    }

    @Override
    public AdUnitResponseVO deleteAdUnit(@NotBlank String unitId) throws AbstractAdPubException {

        AdUnitPO adUnitPO = adUnitDao
            .findById(unitId)
            .orElseThrow(AdUnitSameNameExistException::new);
        adUnitDao.delete(adUnitPO);

        return AdUnitResponseVO.builder()
            .unitId(adUnitPO.getUnitId())
            .unitName(adUnitPO.getUnitName())
            .build();
    }

    @Override
    public AdUnitKeywordResponseVO addAdUnitKeyword(@Valid AdUnitKeywordRequestVO requestVO)
        throws AbstractAdPubException {

        List<String> unitIds = requestVO.getUnitKeywordList().stream().map(UnitKeyword::getUnitId).distinct().collect(
            Collectors.toList());
        if (this.isRelatedUnitAllExist(unitIds)) {
            throw new AdUnitNotFoundException();
        }

        List<AdUnitKeyword> adUnitKeywordList = requestVO.getUnitKeywordList().stream().distinct()
            .map(x -> new AdUnitKeyword(x.getUnitId(), x.getKeyword())).collect(Collectors.toList());

        List<String> adUnitKeywordIds = adUnitKeywordDao.saveAll(adUnitKeywordList)
            .stream().map(AdUnitKeyword::getUnitKeywordId)
            .collect(Collectors.toList());
        return new AdUnitKeywordResponseVO().setUnitKeywordIds(adUnitKeywordIds);
    }

    @Override
    public AdUnitInterestResponseVO addAdUnitInterest(@Valid AdUnitInterestRequestVO requestVO)
        throws AbstractAdPubException {

        List<String> unitIds = requestVO.getUnitInterestList().stream().map(UnitInterest::getUnitId).distinct().collect(
            Collectors.toList());
        if (!this.isRelatedUnitAllExist(unitIds)) {
            throw new AdUnitNotFoundException();
        }

        List<AdUnitInterest> adUnitInterestList = requestVO.getUnitInterestList().stream().distinct()
            .map(x -> new AdUnitInterest(x.getUnitId(), x.getInterestTag())).collect(Collectors.toList());

        List<String> adUnitInterestIds = adUnitInterestDao.saveAll(adUnitInterestList)
            .stream().map(AdUnitInterest::getInterestTag)
            .collect(Collectors.toList());
        return new AdUnitInterestResponseVO().setAdUnitInterestIds(adUnitInterestIds);
    }

    @Override
    public AdUnitDistrictResponseVO addAdUnitDistrict(@Valid AdUnitDistrictRequestVO requestVO)
        throws AbstractAdPubException {
        List<String> unitIds = requestVO.getUnitDistrictList().stream().map(UnitDistrict::getUnitId).distinct().collect(
            Collectors.toList());
        if (!this.isRelatedUnitAllExist(unitIds)) {
            throw new AdUnitNotFoundException();
        }

        List<AdUnitDistrict> adUnitDistrictList = requestVO.getUnitDistrictList()
            .stream().distinct()
            .map(x -> new AdUnitDistrict(x.getUnitId(), x.getState(), x.getProvince(), x.getCity(), x.getCounty()))
            .collect(Collectors.toList());

        List<String> adUnitDistrictIds = adUnitDistrictDao.saveAll(adUnitDistrictList)
            .stream().map(AdUnitDistrict::getDistrictId).collect(
                Collectors.toList());

        return new AdUnitDistrictResponseVO().setAdUnitDistrictIds(adUnitDistrictIds);
    }

    @Override
    public CreativeUnitResponseVO addCreativeUnit(CreativeUnitRequestVO requestVO) throws AbstractAdPubException {

        List<String> creativeIds = requestVO.getCreativeUnitItemList().stream().map(CreativeUnitItem::getCreativeId)
            .distinct().collect(
                Collectors.toList());
        if (!this.isRelatedCreativeAllExist(creativeIds)) {
            throw new CreativeNotFoundException();
        }

        List<CreativeUnitPO> creativeUnitList = requestVO.getCreativeUnitItemList()
            .stream().distinct().map(x -> new CreativeUnitPO(x.getCreativeId(), x.getCreativeUnitId()))
            .collect(Collectors.toList());

        List<String> creativeUnitIds = creativeUnitDao.saveAll(creativeUnitList)
            .stream().map(CreativeUnitPO::getCreativeUnitId)
            .collect(Collectors.toList());
        return new CreativeUnitResponseVO().setCreativeUnitIds(creativeUnitIds);
    }

    /**
     * <h2>校验传入的推广单元识别码是否全部在数据库中存在</h2>
     *
     * @param sourceUnitIds [1] 传入的推广单元识别码
     *
     * @return {@link boolean}
     *
     * @author junG
     * @description 校验传入的推广单元识别码是否全部在数据库中存在 如果至少有一个不存在 则返回是false
     * @date 23:56 2019/5/26
     */
    private boolean isRelatedUnitAllExist(List<String> sourceUnitIds) {

        if (CollectionUtils.isEmpty(sourceUnitIds)) {
            return false;
        }

        List<String> existIds = adUnitDao.findAllById(sourceUnitIds)
            .stream().map(AdUnitPO::getPlanId).collect(Collectors.toList());

        /*
           数据库中存在的推广单元识别码 与传入的推广单元识别码比较
           如果无法全部匹配 说明传入的推广单元存在数据库中没有的非法数据
           则返回 有不存在的推广单元 hasNotExist=true
         */
        boolean allExist = existIds.equals(sourceUnitIds);
        return allExist;
    }

    /**
     * <h2>校验传入的创意识别码是否全部在数据库中存在</h2>
     *
     * @param sourceCreativeIds [1] 传入的推广单元识别码
     *
     * @return {@link boolean}
     *
     * @author junG
     * @description 校验传入的创意识别码是否全部在数据库中存在 如果至少有一个不存在 则返回是false
     * @date 23:56 2019/5/26
     */
    private boolean isRelatedCreativeAllExist(List<String> sourceCreativeIds) {

        if (CollectionUtils.isEmpty(sourceCreativeIds)) {
            return false;
        }

        List<String> existIds = creativeDao.findAllById(sourceCreativeIds)
            .stream().map(CreativePO::getCreativeId).collect(Collectors.toList());

        /*
           数据库中存在的创意识别码 与传入的创意识别码比较
           如果无法全部匹配 说明传入的创意存在数据库中没有的非法数据
           则返回 有不存在的创意 hasNotExist=true
         */
        boolean allExist;
        allExist = existIds.equals(sourceCreativeIds);
        return allExist;
    }
}
